package xyz.wbsite.dbtool.javafx.manger;

import org.springframework.boot.system.ApplicationHome;
import xyz.wbsite.dbtool.javafx.manger.callable.AndroidCallable;
import xyz.wbsite.dbtool.javafx.manger.callable.ApiCallable;
import xyz.wbsite.dbtool.javafx.manger.callable.DocCallable;
import xyz.wbsite.dbtool.javafx.manger.callable.SpringBootCallable;
import xyz.wbsite.dbtool.javafx.manger.callable.VueCallable;
import xyz.wbsite.dbtool.javafx.po.AbstractDBmapper;
import xyz.wbsite.dbtool.javafx.po.AndroidOption;
import xyz.wbsite.dbtool.javafx.po.Api;
import xyz.wbsite.dbtool.javafx.po.Doc;
import xyz.wbsite.dbtool.javafx.po.Field;
import xyz.wbsite.dbtool.javafx.po.FieldType;
import xyz.wbsite.dbtool.javafx.po.Module;
import xyz.wbsite.dbtool.javafx.po.MySQLDBmapper;
import xyz.wbsite.dbtool.javafx.po.OracleDBmapper;
import xyz.wbsite.dbtool.javafx.po.Project;
import xyz.wbsite.dbtool.javafx.po.Table;
import xyz.wbsite.dbtool.javafx.po.VueOption;
import xyz.wbsite.dbtool.javafx.tool.Dialog;
import xyz.wbsite.dbtool.javafx.tool.Tool;

import java.io.File;
import java.io.FilenameFilter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ProjectManager {

    public static boolean isUpdate = false;
    private XmlManager xmlService;
    private JsonManager jsonManager;
    public static File path;
    private ExecutorService service = Executors.newCachedThreadPool();
    private Project project = new Project();

    public ProjectManager() {
        xmlService = ManagerFactory.getXmlManager();
        jsonManager = ManagerFactory.getJsonManager();
        try {
            ApplicationHome home = new ApplicationHome(getClass());
            File jarFile = home.getSource();
            this.path = new File(jarFile.getParent(), "project");
        } catch (Exception e) {
            e.printStackTrace();
            this.path = new File(System.getProperty("user.home"), "project");
        }
        if (!path.exists()) {
            path.mkdirs();
        }
    }

    public static File getPath() {
        return path;
    }

    public static void setPath(File path) {
        ProjectManager.path = path;
    }

    public Module findDBByDBName(String name) {
        for (Module md : project.getModules()) {
            if (md.getName().equals(name)) {
                return md;
            }
        }
        return null;
    }

    public boolean removeDBByDBName(String name) {
        for (Module md : project.getModules()) {
            if (md.getName().equals(name)) {
                project.getModules().remove(md);
                return true;
            }
        }
        return false;
    }

    public Table findTableByTableName(Module md, String name) {
        for (Table t : md.getTables()) {
            if (t.getTableName().equals(name)) {
                return t;
            }
        }
        return null;
    }

    public void checkSysFields(Table table) {
        if (table != null) {
            if (table.getSys()) {
                removeSysFields(table);
                insertSysFields(table);
            } else {
                removeSysFields(table);
            }
        }
    }

    private void removeSysFields(Table table) {
        Iterator<Field> iterator = table.getFields().iterator();
        while (iterator.hasNext()) {
            Field next = iterator.next();
            if (next.getIsSystem()) {
                iterator.remove();
            }
        }
    }

    private void insertSysFields(Table table) {
        Field id = new Field("ID");
        id.setIsSystem(true);
        id.setIsMust(true);
        id.setIsPrimaryKey(true);
        id.setFieldType(FieldType.Long.name());
        id.setFieldLength(19);
        id.setFieldComment("主键");

        Field row_version = new Field("ROW_VERSION");
        row_version.setIsSystem(true);
        row_version.setIsMust(true);
        row_version.setFieldType(FieldType.Long.name());
        row_version.setFieldLength(19);
        row_version.setFieldComment("行版本");

        Field is_deleted = new Field("IS_DELETED");
        is_deleted.setIsSystem(true);
        is_deleted.setIsMust(true);
        is_deleted.setDefaultValue("0");
        is_deleted.setFieldType(FieldType.Boolean.name());
        is_deleted.setFieldLength(1);
        is_deleted.setFieldComment("是否已删除");

        Field created_by = new Field("CREATE_BY");
        created_by.setIsSystem(true);
        created_by.setIsMust(true);
        created_by.setFieldType(FieldType.Long.name());
        created_by.setFieldLength(19);
        created_by.setFieldComment("创建用户");

        Field creation_time = new Field("CREATE_TIME");
        creation_time.setIsSystem(true);
        creation_time.setIsMust(true);
        creation_time.setDefaultValue("NULL");
        creation_time.setFieldType(FieldType.Date.name());
        creation_time.setFieldComment("创建时间");

        Field last_updated_by = new Field("LAST_UPDATE_BY");
        last_updated_by.setIsSystem(true);
        creation_time.setDefaultValue("NULL");
        last_updated_by.setFieldType(FieldType.Long.name());
        last_updated_by.setFieldLength(19);
        last_updated_by.setFieldComment("最后更新用户");

        Field last_update_time = new Field("LAST_UPDATE_TIME");
        last_update_time.setIsSystem(true);
        last_update_time.setFieldType(FieldType.Date.name());
        last_update_time.setFieldComment("最后更新时间");

        table.putFirstField(id);
        table.putField(row_version);
        table.putField(is_deleted);
        table.putField(created_by);
        table.putField(creation_time);
        table.putField(last_updated_by);
        table.putField(last_update_time);
    }

    public Module newModule() {
        String base = "example";
        String name = base;
        int k = 0;
        do {
            int i;
            for (i = 0; i < project.getModules().size(); i++) {
                if (name.equals(project.getModules().get(i).getName())) {
                    break;
                }
            }
            if (i < project.getModules().size()) {
                k++;
                name = base + k;
            } else {
                Module md = new Module(name);
                md.setName(name);
                md.setNote("注释");
                md.setNeedGenerate(true);
                project.putModule(md);
                return md;
            }
        } while (true);
    }

    public Table newTable(Module md) {
        String base = "NEW_TABLE";
        String name = base;
        int k = 0;
        do {
            int i;
            for (i = 0; i < md.getTables().size(); i++) {
                if (name.equals(md.getTables().get(i).getTableName())) {
                    break;
                }
            }
            if (i < md.getTables().size()) {
                k++;
                name = base + "_" + k;
            } else {
                Table table = new Table(name);
                table.setTableComment("注释");
                table.setCreate(true);
                table.setDelete(true);
                table.setUpdate(true);
                table.setFind(true);
                table.setGet(false);
                table.setSearch(false);
                table.setPage(true);
                table.setAjax(true);
                table.setPageAdd(true);
                table.setPageDel(true);
                table.setPageImp(true);
                table.setPageExp(true);
                checkSysFields(table);// 检查或移除系统字段
                md.putTable(table);
                return table;
            }
        } while (true);
    }

    public int getNewFieldName(List<Field> fields) {
        String base = "NEW_FIELD";
        String name = base;
        int k = 0;
        do {
            int i;
            for (i = 0; i < fields.size(); i++) {
                if (name.equals(fields.get(i).getFieldName())) {
                    break;
                }
            }
            if (i < fields.size()) {
                k++;
                name = base + "_" + k;
            } else {
                Field field = new Field(name);
                field.setDefaultValue("NULL");
                field.setIsQuery(false);
                field.setIsMust(false);
                field.setIsPrimaryKey(false);
                field.setFieldType(FieldType.String_var50.name());
                field.setFieldLength(FieldType.String_var50.getDefaultLength());
                int j = 0;
                for (; j < fields.size(); j++) {
                    if (fields.get(j).getFieldName().equals("ROW_VERSION")) {
                        break;
                    }
                }
                fields.add(j, field);
                return j;
            }
        } while (true);
    }

    public List<Module> getMds() {
        return project.getModules();
    }

    public Project getProject() {
        return project;
    }

    public boolean doCheck() {
        if (project.getName() == null || "".equals(project.getName())) {
            Dialog.showYesNo("没有填写项目名!");
            return false;
        } else if (project.getDomain() == null || "".equals(project.getDomain())) {
            Dialog.showYesNo("没有填写基本域名!");
            return false;
        } else if (project.getAuthor() == null || "".equals(project.getAuthor())) {
            Dialog.showYesNo("没有填写作者!");
            return false;
        }
        for (Module md : project.getModules()) {
            if (md.getName() == null || "".equals(md.getName())) {
                Dialog.showYesNo("项目" + project.getName() + "没有填写模块名!");
                return false;
            } else if (md.getNote() == null || "".equals(md.getNote())) {
                Dialog.showYesNo("项目" + project.getName() + "没有模块注释!");
                return false;
            }
        }
        return true;
    }

    /**
     * 保存配置文件
     */
    public void save(File file) {
        xmlService.saveAs(file, project);
//        jsonManager.saveAs(file, project);
    }

    public void save() {
        xmlService.saveAs(path, project);
//        jsonManager.saveAs(path, project);
    }

    public void delete() {
        File file = new File(path, project.getName() + ".xml");
        if (file.exists()) {
            file.delete();
        }
    }

    /**
     * 生成模板 入口
     *
     * @param root
     */
    public void generate(Project project, final File root) {
        if (!root.exists()) {
            Dialog.showYesNo("目录不存在!");
            return;
        }
        Dialog.showWait("生成中...");

        service.execute(new Runnable() {
            @Override
            public void run() {
                Callable callback = new SpringBootCallable(project, path);

                Future submit = service.submit(callback);
                try {
                    submit.get();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
                Dialog.stopWait();
                Dialog.showSuccess("生成完毕.");
            }
        });
    }

    public void generateApi(File module, File api, List<String> domainList, List<Api> apis) {
        if (module.exists()) {
            Dialog.showWait("生成中...");

            service.execute(new Runnable() {
                @Override
                public void run() {
                    boolean mkdirs = api.mkdirs();
                    ApiCallable apiCallable = new ApiCallable(module, api, domainList, apis);
                    Future submit = service.submit(apiCallable);
                    try {
                        Boolean b = (Boolean) submit.get();
                        if (!b) {
                            Dialog.showError("请确认目录结构是否存在或正确!");
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (ExecutionException e) {
                        e.printStackTrace();
                    }

                    Dialog.stopWait();
                    Dialog.showSuccess("Api生成完毕.");
                }
            });
        }
    }

    public void generateDoc(File module, File api, List<Doc> apis) {
        if (module.exists()) {
            Dialog.showWait("生成中...");

            service.execute(new Runnable() {
                @Override
                public void run() {
                    boolean mkdirs = api.mkdirs();
                    DocCallable docCallable = new DocCallable(module, api, apis);
                    Future submit = service.submit(docCallable);
                    try {
                        Boolean b = (Boolean) submit.get();
                        if (!b) {
                            Dialog.showError("请确认目录结构是否存在或正确!");
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (ExecutionException e) {
                        e.printStackTrace();
                    }

                    Dialog.stopWait();
                    Dialog.showSuccess("Doc生成完毕.");
                }
            });
        }
    }


    public void generate(final String path, AndroidOption option) {
        Dialog.showWait("生成中...");
        new Thread() {
            @Override
            public void run() {
                AndroidCallable androidCallable = new AndroidCallable(path, option);

                Future submit = service.submit(androidCallable);
                try {
                    Boolean b = (Boolean) submit.get();
                    if (!b) {
                        Dialog.showError("请确认目录结构是否存在或正确!");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }

                Dialog.stopWait();
                Dialog.showSuccess("Android生成完毕.");
            }
        }.start();
    }

    public void generate(final String path, VueOption option) {
        Dialog.showWait("生成中...");
        new Thread() {
            @Override
            public void run() {
                VueCallable vueCallable = new VueCallable(path, option);

                Future submit = service.submit(vueCallable);
                try {
                    Boolean b = (Boolean) submit.get();
                    if (!b) {
                        Dialog.showError("请确认目录结构是否存在或正确!");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }

                Dialog.stopWait();
                Dialog.showSuccess("Vue生成完毕.");
            }
        }.start();
    }

    /**
     * 初始化模型树，当存在多个时，默认第一个
     */
    public Project invalidate() {
        File[] files = path.listFiles(new FilenameFilter() {
            public boolean accept(File dir, String name) {
                return name.endsWith(".xml") || name.endsWith(".XML");
            }
        });

        if (files == null || files.length == 0) {
            project = xmlService.inflate(null);
        } else {
            project = xmlService.inflate(files[0]);
        }
        return project;
    }

    /**
     * 初始化模型树，当存在多个时，默认第一个
     */
    public Project invalidate(File xmlFile) {
        project = xmlService.inflate(xmlFile);
        return project;
    }

    public static boolean testConnect(Map<String, String> properties) {
        String url = properties.get("url");
        String username = properties.get("username");
        String password = properties.get("password");
        String driverClassName = properties.get("driverClassName");

        try {
            //加载MySQL的驱动类
            Class.forName(driverClassName);

            Connection cn = DriverManager.getConnection(url, username, password);
            cn.close();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    public static boolean loadMD(Map<String, String> properties) {

        String type = properties.get("type");
        String url = properties.get("url");
        String username = properties.get("username");
        String password = properties.get("password");
        String driverClassName = properties.get("driverClassName");
        Connection cn = null;

        if ("Oracle".equals(type)) {
            try {
                AbstractDBmapper dBmapper = new OracleDBmapper();

                //加载驱动类
                Class.forName(driverClassName);
                cn = DriverManager.getConnection(url, username, password);

                Module md = new Module(username);
                //查询所有表
                Statement statement = cn.createStatement();
                ResultSet rs = statement.executeQuery("select t.table_name,c.comments from user_tables t LEFT JOIN user_tab_comments c ON t.table_name = c.table_name ORDER BY T .table_name");

                while (rs.next()) {
                    Table table = new Table(rs.getString("table_name"), rs.getString("comments"));
                    table.setCreate(true);
                    table.setDelete(true);
                    table.setUpdate(true);
                    table.setFind(true);
                    table.setGet(false);
                    table.setSearch(false);
                    md.putTable(table);
                }

                for (Table table : md.getTables()) {
                    String sql = "SELECT T.*,CASE WHEN C.POSITION='1' THEN '1' ELSE '0' END PrimaryKey FROM(select A.COLUMN_ID,A.COLUMN_NAME,A.DATA_TYPE,A.DATA_LENGTH,A .DATA_PRECISION,A .DATA_SCALE,A.NULLABLE,A.DATA_DEFAULT,B.comments from user_tab_columns A ,user_col_comments B where A.Table_Name='" + table.getTableName() + "' AND B.Table_Name='" + table.getTableName() + "' AND A.COLUMN_NAME=B.COLUMN_NAME) T LEFT JOIN user_cons_columns C ON T.COLUMN_NAME = C.COLUMN_NAME AND POSITION = '1' AND C.Table_Name='" + table.getTableName() + "' ORDER BY T .COLUMN_ID";
                    ResultSet set = statement.executeQuery(sql);

                    while (set.next()) {
                        Field field = new Field(set.getString("COLUMN_NAME"));
                        String data_type = set.getString("DATA_TYPE");
                        int data_length = set.getInt("DATA_LENGTH");
                        int data_precision = set.getInt("DATA_PRECISION");
                        int data_scale = set.getInt("DATA_SCALE");
                        field.setFieldType(dBmapper.getType(data_type, data_length, data_precision, data_scale));
                        field.setFieldLength(set.getInt("DATA_LENGTH"));
                        String nullable = set.getString("NULLABLE");
                        field.setDefaultValue(set.getString("DATA_DEFAULT"));
                        field.setFieldComment(set.getString("COMMENTS"));
                        if ("N".equals(nullable)) {
                            field.setIsMust(true);
                        } else {
                            field.setIsMust(false);
                        }
                        String primarykey = set.getString("PRIMARYKEY");
                        if ("1".equals(primarykey)) {
                            field.setIsPrimaryKey(true);
                        } else {
                            field.setIsPrimaryKey(false);
                        }
                        field.setIsQuery(false);

                        table.putField(field);
                    }
                }

                ManagerFactory.getProjectManager().project.putModule(md);
                isUpdate = true;
                return true;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    cn.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        } else if ("Mysql".equals(type)) {
            try {
                AbstractDBmapper dBmapper = new MySQLDBmapper();

                //加载驱动类
                Class.forName(driverClassName);
                cn = DriverManager.getConnection(url, username, password);

                String[] split = url.split("/");
                String dbName = split[split.length - 1];

                Module md = new Module(dbName);
                //查询所有表
                Statement statement = cn.createStatement();
                ResultSet rs = statement.executeQuery("SELECT TABLE_NAME,TABLE_COMMENT FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '" + dbName + "'");

                while (rs.next()) {
                    Table table = new Table(rs.getString("TABLE_NAME"), rs.getString("TABLE_COMMENT"));
                    table.setCreate(true);
                    table.setDelete(true);
                    table.setUpdate(true);
                    table.setFind(true);
                    table.setGet(false);
                    table.setSearch(false);
                    md.putTable(table);
                }

                for (Table table : md.getTables()) {
                    String sql = "select COLUMN_NAME,COLUMN_COMMENT,COLUMN_KEY,COLUMN_DEFAULT,IS_NULLABLE,DATA_TYPE, COLUMN_TYPE,CHARACTER_MAXIMUM_LENGTH from information_schema.COLUMNS where table_name = '" + table.getTableName() + "' and table_schema = '" + dbName + "' ORDER BY ORDINAL_POSITION ASC";
                    ResultSet set = statement.executeQuery(sql);

                    while (set.next()) {
                        Field field = new Field(set.getString("COLUMN_NAME"));
                        if ("NO".equals(set.getString("IS_NULLABLE"))) {
                            field.setIsMust(true);
                        } else {
                            field.setIsMust(false);
                        }

                        if ("PRI".equals(set.getString("COLUMN_KEY"))) {
                            field.setIsPrimaryKey(true);
                        } else {
                            field.setIsPrimaryKey(false);
                        }

                        String data_type = set.getString("DATA_TYPE");
                        int data_length = set.getInt("CHARACTER_MAXIMUM_LENGTH");
                        field.setFieldType(dBmapper.getType(data_type, data_length, 0, 0));
                        field.setFieldLength(0);
                        if ("varchar".equals(data_type) || "char".equals(data_type)) {
                            int length = Integer.parseInt(set.getString("CHARACTER_MAXIMUM_LENGTH"));
                            field.setFieldLength(length);
                        }

                        field.setDefaultValue(set.getString("COLUMN_DEFAULT"));
                        field.setFieldComment(set.getString("COLUMN_COMMENT"));

                        field.setIsQuery(false);

                        table.putField(field);
                    }
                }

                ManagerFactory.getProjectManager().project.putModule(md);
                isUpdate = true;
                return true;
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return false;
        }
        return false;
    }
}
